package com.us.usws.support.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.iwsh.core.common.GlobalConfig;
import net.iwsh.core.common.Wildcard;
import net.iwsh.core.exception.GenericRuntimeException;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.us.usws.biz.LoginOutWorker;
import com.us.usws.common.EpServerConstants;
import com.us.usws.common.SiteConstants;
import com.us.usws.model.flat.epserver.DOEpTokens;
import com.us.usws.model.flat.system.DOUser;
import com.us.usws.service.ro.EpTokensROService;
import com.us.usws.service.ro.UserROService;
import com.us.usws.support.uitls.HttpServletUtils;
import com.us.usws.support.uitls.ServiceFactory;
import com.us.usws.support.uitls.SiteWebContext;

/**
 * ep server filter
 * @author Wang Jianbo
 */
public class EpDeviceLoginFilter implements Filter {

	private static Log log = LogFactory.getLog(EpDeviceLoginFilter.class);

	private String ignoreExp = "";

	private EpTokensROService epService;
	private UserROService uServcie;

	/**
	 * The filter configuration object we are associated with. If this value is
	 * null, this filter instance is not currently configured.
	 */
	protected FilterConfig filterConfig = null;

	/**
	 * Take this filter out of service.
	 */
	public void destroy() {
		this.filterConfig = null;
	}

	/**
	 * Select and set (if specified) the character encoding to be used to
	 * interpret request parameters for this request.
	 *
	 * @param request  The servlet request we are processing
	 * @param response The servlet response we are creating
	 * @param chain	The filter chain we are processing
	 * @throws IOException	  if an input/output error occurs
	 * @throws ServletException if a servlet error occurs
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		epService = ServiceFactory.getInstance().getService(EpTokensROService.class);
		uServcie = ServiceFactory.getInstance().getService(UserROService.class);

		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;

		//check whether the url should be ignored.
		String path = req.getRequestURI();
		if (shouldIgnore(path)) {
			log.debug("{EPLoginFilter} :: Ignore URL: [" + path + "].");
			chain.doFilter(request, response);
			return;
		}

		HttpServletUtils.debugHeaders(req);

		//determine ep user in webcontext
		SiteWebContext wc = new SiteWebContext(req, resp);
		//get token in header
		String token = wc.getEpToken();
		String userId = wc.getEpUserId();

		//if not logged-in yet
		boolean needLogin = needLogin(token, userId);

		//for debug purpose
		if (SiteConstants.IS_DEBUG) {
			fakeLogin();
			chain.doFilter(request, response);
			return;
		}

		//need login
		if (needLogin) {
			req.getRequestDispatcher("/api/ebox/my_accounts/needLogin.json").forward(request, response);
			//resp.sendRedirect("/api/ebox/my_accounts/needLogin.json");
			return;
		} else {
			chain.doFilter(request, response);
			return;
		}
	}

	/**
	 * Check whether str matches the regex
	 *
	 * @param str   servlet path to be checked
	 * @return true if matches, false if not.
	 */
	private boolean shouldIgnore(String str) {
		if (ignoreExp == null) {
			return false;
		}
		return Wildcard.matchesIgnoreCase(ignoreExp, str, ",");
	}

	/**
	 * fake login
	 * @param wc
	 */
	public void fakeLogin() {
		String autoToken = GlobalConfig.getString("Commons/AutoLoginEPToken");
		String autoLoginName = GlobalConfig.getString("Commons/AutoLoginEPLoginName");

		DOEpTokens ept = epService.getEpTokens(autoToken);
		DOUser du = uServcie.getUserByLoginName(autoLoginName);
		log.debug("{EPLoginFilter} :: Auto login with loginName=[" + autoLoginName + "], token=[" + autoToken + "]");
		if (ept == null || du == null) {
			throw new GenericRuntimeException("Cannot auto login. Add config first!");
		}

		//set to web context
		LoginOutWorker.epDeviceLogin(autoToken, ept.getId());

	}

	/**
	 * check whether need login
	 * @param eu
	 * @param boxsn
	 * @param token
	 * @return
	 */
	private boolean needLogin(String token, String userId) {
		if (StringUtils.isBlank(token)) {
			log.debug("{EPLoginFilter} :: Empty of token.");
			return true;
		}

		if (StringUtils.isBlank(userId)) {
			log.debug("{EPLoginFilter} :: Empty of userid.");
			return true;
		}

		DOUser u = uServcie.getDOUser(userId);
		if (u == null) {
			log.debug("{EPLoginFilter} :: Invalid user of userId={" + userId + "}");
			return true;
		}

		DOEpTokens ept = epService.getEpTokens(token);
		if (ept == null) {
			log.debug("{EPLoginFilter} :: No DoEpTokens db DB with token=[" + token + "].");
			return true;
		}

		//whether time's over
		long duration = (System.currentTimeMillis() - ept.getHeartbeatAt().getTime()) / 60000;
		long limit = EpServerConstants.EP_HEARTBEAT_IMIT;
		log.debug("{EPLoginFilter} :: Get loginAt= [" + DateFormatUtils.format(ept.getLoginAt(), "yy-MM-dd HH:mm:ss") + "], heartbeat= ["
				+ DateFormatUtils.format(ept.getHeartbeatAt(), "yy-MM-dd HH:mm:ss") + "], durationi= [" + duration + "], while limit= ["
				+ limit + "].");

		if (duration > limit) {
			log.debug("{EPLoginFilter} :: Heartbeat time's over.");
			return true;
		}

		//whether has user_no in db
		if (ept.getUserNo() == null || ept.getUserNo().trim().length() == 0) {
			log.debug("{EPLoginFilter} :: No user_no in ep_tokens, need login first.");
			return true;
		}

		return false;
	}

	/**
	 * Place this filter into service.
	 *
	 * @param filterConfig The filter configuration object
	 */
	public void init(FilterConfig filterConfig) throws ServletException {

		this.filterConfig = filterConfig;
		this.ignoreExp = filterConfig.getInitParameter("ignoreExp");
	}

	/**
	 * Returns the filterConfig.
	 *
	 * @return FilterConfig
	 */
	public FilterConfig getFilterConfig() {
		return filterConfig;
	}

	/**
	 * Sets the filterConfig.
	 *
	 * @param filterConfig The filterConfig to set
	 */
	public void setFilterConfig(FilterConfig filterConfig) {
		this.filterConfig = filterConfig;
	}

}
